﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Orchard;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData.Models;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Security;
using Orchard.Users.Services;
using RaisingStudio.ContentTypesApi.Services;

namespace RaisingStudio.ContentTypesApi.Controllers
{
    public class ContentTypesController : ApiController
    {
        private static readonly char[] separator = new[] { '{', '}', ',' };
        private readonly IAuthenticationService _authenticationService;
        private readonly IMembershipService _membershipService;
        private readonly IUserService _userService;
        private readonly IOrchardServices _orchardServices;
        private readonly IContentManager _contentManager;
        private readonly IContentTypesService _contentTypesService;

        public ContentTypesController(
            IAuthenticationService authenticationService,
            IMembershipService membershipService,
            IUserService userService,
            IOrchardServices orchardServices,
            IContentManager contentManager,
            IContentTypesService contentTypeService)
        {
            _authenticationService = authenticationService;
            _membershipService = membershipService;
            _userService = userService;
            _orchardServices = orchardServices;
            _contentManager = contentManager;
            _contentTypesService = contentTypeService;

            Logger = NullLogger.Instance;
            T = NullLocalizer.Instance;
        }

        public ILogger Logger { get; set; }
        public Localizer T { get; set; }


        public static IEnumerable ConvertToRows(IEnumerable<Models.ContentItem> source, string[] properties = null)
        {
            foreach (var item in source)
            {
                object[] partsValues = GetPartsValues(item, properties);
                object[] fieldsValues = GetFieldsValues(item, properties);
                object[] values = new object[partsValues.Length + fieldsValues.Length];
                Array.Copy(partsValues, values, partsValues.Length);
                Array.Copy(fieldsValues, 0, values, partsValues.Length, fieldsValues.Length);
                yield return new { id = item.Id, cell = values };
            }
        }

        private static object[] GetPartsValues(Models.ContentItem item, string[] properties)
        {
            var hasProperties = (properties != null) && (properties.Length > 0);
            if (hasProperties)
            {
                List<object> partsValues = new List<object>();
                partsValues.Add(item.Id);
                if (properties.Contains("Title"))
                {
                    partsValues.Add(item.Title);
                }
                if (properties.Contains("Body"))
                {
                    partsValues.Add(item.Body);
                }
                if (properties.Contains("Owner"))
                {
                    partsValues.Add(item.Owner);
                }
                if (properties.Contains("CreateTime"))
                {
                    partsValues.Add(item.CreateTime);
                }
                return partsValues.ToArray();
            }
            else
            {
                object[] partsValues = new object[]
                        {
                            item.Id,
                            item.Title,
                            item.Body,
                            item.Owner,
                            item.CreateTime
                        };
                return partsValues;
            }
        }

        private static object[] GetFieldsValues(Models.ContentItem item, string[] properties)
        {
            var hasProperties = (properties != null) && (properties.Length > 0);
            if (hasProperties)
            {
                List<object> fieldsValues = new List<object>();
                foreach (var fieldName in item.Fields.Keys)
                {
                    if (properties.Contains(fieldName))
                    {
                        fieldsValues.Add(item.Fields[fieldName]);
                    }
                }
                return fieldsValues.ToArray();
            }
            else
            {
                return item.Fields.Values.ToArray();
            }
        }


        // GET api/<controller>
        public IEnumerable<string> Get()
        {
            return _contentTypesService.GetContentTypes();
        }

        // GET api/<controller>?type=...
        public ContentTypeDefinition GetContentTypeDefinition(string type)
        {
            return (from t in _contentManager.GetContentTypeDefinitions()
                    where t.Name == type
                    select t).FirstOrDefault();
        }

        // GET api/<controller>/5
        public Models.ContentItem Get(int id, [FromBody]string properties)
        {
            return _contentTypesService.Get(id, string.IsNullOrWhiteSpace(properties) ? null : properties.Split(separator, StringSplitOptions.RemoveEmptyEntries));
        }

        // GET api/<controller>?name=...
        public IEnumerable<Models.ContentItem> Get(string name, [FromBody]string properties, int? rows = null, int? page = null)
        {
            int defaultRows = 10;
            var query = _contentTypesService.Query(name, string.IsNullOrWhiteSpace(properties) ? null : properties.Split(separator, StringSplitOptions.RemoveEmptyEntries));
            if (page != null)
            {
                return query.Skip(((page ?? 1) - 1) * (rows ?? defaultRows))
                                .Take(rows ?? defaultRows);
            }
            else
            {
                return query;
            }
        }

        // GET api/<controller>?name=...&_search=...
        public object Get(string name, bool? _search, int? nd, string sidx, string sord, [FromBody]string properties, int? rows = 10, int? page = 1)
        {
            int count;
            var data = _contentTypesService.Query(name, null, null, null, null, string.IsNullOrWhiteSpace(sidx) ? null : new[] { sidx }, new[] { sord }, ((int)page - 1) * (int)rows, (int)rows, out count, string.IsNullOrWhiteSpace(properties) ? null : properties.Split(separator, StringSplitOptions.RemoveEmptyEntries));
            return new
            {
                total = Math.Ceiling((float)count / (float)rows),
                page = page,
                records = count,
                rows = ConvertToRows(data)
            };
        }

        // GET api/<controller>?name=...&_search=...
        public object Get(string name, bool? _search, int? nd, string sidx, string sord, string searchField, string searchString, string searchOper, [FromBody]string properties, string filters = null, int? rows = 10, int? page = 1)
        {
            string[] searchColumns = (_search ?? false) ? new[] { searchField } : null;
            string[] searchOperators = (_search ?? false) ? new[] { searchOper } : null;
            string[] searchValues = (_search ?? false) ? new[] { searchString } : null;
            int count;
            var data = _contentTypesService.Query(name, searchColumns, searchOperators, searchValues, null, string.IsNullOrWhiteSpace(sidx) ? null : new[] { sidx }, new[] { sord }, ((int)page - 1) * (int)rows, (int)rows, out count, string.IsNullOrWhiteSpace(properties) ? null : properties.Split(separator, StringSplitOptions.RemoveEmptyEntries));
            return new
            {
                total = Math.Ceiling((float)count / (float)rows),
                page = page,
                records = count,
                rows = ConvertToRows(data)
            };
        }

        // POST api/<controller>?name=...
        public int Post([FromBody]Models.ContentItem value, string name, string properties = null)
        {
            return _contentTypesService.Save(value, name, string.IsNullOrWhiteSpace(properties) ? null : properties.Split(separator, StringSplitOptions.RemoveEmptyEntries));
        }

        // PUT api/<controller>/5
        public int Put(int id, [FromBody]Models.ContentItem value, string properties = null)
        {
            return _contentTypesService.Update(id, value, string.IsNullOrWhiteSpace(properties) ? null : properties.Split(separator, StringSplitOptions.RemoveEmptyEntries));
        }

        // DELETE api/<controller>/5
        public int Delete(int id)
        {
            return _contentTypesService.Delete(id);
        }
    }
}